package pers.dawnyang.modular.monitor.controller;

import java.lang.management.ManagementFactory;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Properties;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import oshi.SystemInfo;
import oshi.hardware.CentralProcessor;
import oshi.hardware.CentralProcessor.TickType;
import oshi.hardware.GlobalMemory;
import oshi.hardware.HardwareAbstractionLayer;
import oshi.software.os.FileSystem;
import oshi.software.os.OSFileStore;
import oshi.software.os.OperatingSystem;
import oshi.util.Util;
import pers.dawnyang.common.domain.Result;
import pers.dawnyang.common.util.ResultUtil;
import pers.dawnyang.common.util.date.DateUtil;
import pers.dawnyang.common.util.math.UnitUtil;
import pers.dawnyang.common.util.web.IPUtil;
import pers.dawnyang.modular.monitor.domain.response.server.CpuInfo;
import pers.dawnyang.modular.monitor.domain.response.server.DiskInfo;
import pers.dawnyang.modular.monitor.domain.response.server.JvmInfo;
import pers.dawnyang.modular.monitor.domain.response.server.RamInfo;
import pers.dawnyang.modular.monitor.domain.response.server.ServerInfo;

/**
 * 服务器硬件资源监控
 * 
 * @author dawn yang
 * @date 2021/04/09
 */

@RequestMapping("/monitor/server")
@Controller
public class ServerController {

  private static final int OSHI_WAIT_SECOND = 1000;

  private SystemInfo si = new SystemInfo();

  private HardwareAbstractionLayer hal = si.getHardware();

  @RequiresPermissions("monitor:server:index")
  @GetMapping("/index")
  public String index() {
    return "/monitor/server/index";
  }

  @RequiresPermissions("monitor:server:list")
  @GetMapping("/cpuInfo")
  @ResponseBody
  public Result<List<CpuInfo>> cpuInfo() {
    List<CpuInfo> resList = new ArrayList<>();
    CpuInfo cpu = new CpuInfo();
    CentralProcessor processor = hal.getProcessor();
    long[] prevTicks = processor.getSystemCpuLoadTicks();
    Util.sleep(OSHI_WAIT_SECOND);
    long[] ticks = processor.getSystemCpuLoadTicks();
    long nice = ticks[TickType.NICE.getIndex()] - prevTicks[TickType.NICE.getIndex()];
    long irq = ticks[TickType.IRQ.getIndex()] - prevTicks[TickType.IRQ.getIndex()];
    long softirq = ticks[TickType.SOFTIRQ.getIndex()] - prevTicks[TickType.SOFTIRQ.getIndex()];
    long steal = ticks[TickType.STEAL.getIndex()] - prevTicks[TickType.STEAL.getIndex()];
    long cSys = ticks[TickType.SYSTEM.getIndex()] - prevTicks[TickType.SYSTEM.getIndex()];
    long user = ticks[TickType.USER.getIndex()] - prevTicks[TickType.USER.getIndex()];
    long iowait = ticks[TickType.IOWAIT.getIndex()] - prevTicks[TickType.IOWAIT.getIndex()];
    long idle = ticks[TickType.IDLE.getIndex()] - prevTicks[TickType.IDLE.getIndex()];
    long totalCpu = user + nice + cSys + idle + iowait + irq + softirq + steal;
    cpu.setCpuNum(processor.getLogicalProcessorCount());
    String sysUsage = new DecimalFormat("#.##%").format(cSys * 1.0 / totalCpu);
    String useUsage = new DecimalFormat("#.##%").format(user * 1.0 / totalCpu);
    String waitRate = new DecimalFormat("#.##%").format(iowait * 1.0 / totalCpu);
    String totalUsage = new DecimalFormat("#.##%").format(1.0 - (idle * 1.0 / totalCpu));
    cpu.setTotalUsage(totalUsage);
    cpu.setSysUsage(sysUsage);
    cpu.setUseUsage(useUsage);
    cpu.setWaitRate(waitRate);
    resList.add(cpu);
    return ResultUtil.success(resList);
  }

  @RequiresPermissions("monitor:server:list")
  @GetMapping("/ramInfo")
  @ResponseBody
  public Result<List<RamInfo>> ramInfo() {
    List<RamInfo> resList = new ArrayList<>();
    GlobalMemory memory = hal.getMemory();
    RamInfo ram = new RamInfo();
    long total = memory.getTotal();
    long available = memory.getAvailable();
    long used = total - available;
    ram.setTotal(UnitUtil.formatByte(total));
    ram.setUsed(UnitUtil.formatByte(used));
    ram.setFree(UnitUtil.formatByte(available));
    ram.setUsage(new DecimalFormat("#.##%").format((total - available) * 1.0 / total));
    resList.add(ram);
    return ResultUtil.success(resList);
  }

  @RequiresPermissions("monitor:server:list")
  @GetMapping("/diskInfo")
  @ResponseBody
  public Result<List<DiskInfo>> diskInfo() {
    List<DiskInfo> resList = new ArrayList<>();
    SystemInfo si = new SystemInfo();
    OperatingSystem os = si.getOperatingSystem();
    FileSystem fileSystem = os.getFileSystem();
    List<OSFileStore> fsArray = fileSystem.getFileStores();
    for (OSFileStore fs : fsArray) {
      long free = fs.getUsableSpace();
      long total = fs.getTotalSpace();
      long used = total - free;
      DiskInfo diskInfo = new DiskInfo();
      diskInfo.setDrivePath(fs.getMount());
      diskInfo.setFileSys(fs.getType());
      diskInfo.setDriveType(fs.getName());
      diskInfo.setTotalSize(UnitUtil.formatByte(total));
      diskInfo.setFreeSize(UnitUtil.formatByte(free));
      diskInfo.setUsedSize(UnitUtil.formatByte(used));
      diskInfo.setUsage(new DecimalFormat("#.##%").format(used * 1.0 / total));
      resList.add(diskInfo);
    }
    return ResultUtil.success(resList);
  }

  @RequiresPermissions("monitor:server:list")
  @GetMapping("/serverInfo")
  @ResponseBody
  public Result<List<ServerInfo>> serverInfo() {
    List<ServerInfo> resList = new ArrayList<>();
    ServerInfo serverInfo = new ServerInfo();
    Properties props = System.getProperties();
    serverInfo.setServerName(IPUtil.getHostName());
    serverInfo.setOsName(props.getProperty("os.name"));
    serverInfo.setServerIP(IPUtil.getHostIp());
    serverInfo.setOsArch(props.getProperty("os.arch"));
    resList.add(serverInfo);
    return ResultUtil.success(resList);
  }

  @RequiresPermissions("monitor:server:list")
  @GetMapping("/jvmInfo")
  @ResponseBody
  public Result<List<JvmInfo>> jvmInfo() {
    List<JvmInfo> resList = new ArrayList<>();
    JvmInfo jvm = new JvmInfo();
    Properties props = System.getProperties();
    long totalMemory = Runtime.getRuntime().totalMemory();
    long maxMemory = Runtime.getRuntime().maxMemory();
    long freeMemory = Runtime.getRuntime().freeMemory();
    long usedMemory = totalMemory - freeMemory;
    jvm.setTotalMemory(UnitUtil.formatByte(totalMemory));
    jvm.setMaxMemory(UnitUtil.formatByte(maxMemory));
    jvm.setFreeMemory(UnitUtil.formatByte(freeMemory));
    jvm.setUsedMemory(UnitUtil.formatByte(usedMemory));
    String usage =
        new DecimalFormat("#.##%").format((totalMemory - freeMemory) * 1.0 / totalMemory);
    jvm.setUsage(usage);
    jvm.setJavaVersion(props.getProperty("java.version"));
    jvm.setJavaHome(props.getProperty("java.home"));
    jvm.setProjectPath(props.getProperty("user.dir"));
    jvm.setJavaName(ManagementFactory.getRuntimeMXBean().getVmName());
    Date serverStartDate = DateUtil.getServerStartDate();
    SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    jvm.setStartTime(formatter.format(serverStartDate));
    Date nowTime = new Date();
    String runTime = DateUtil.getDatePoor(nowTime, serverStartDate);
    jvm.setRunTime(runTime);
    resList.add(jvm);
    return ResultUtil.success(resList);
  }


}
